import numpy as np

try:
    from TestToolApi import Debug
except ModuleNotFoundError:
    from .TestToolApi import Debug
from queue import Queue as ThreadQueue
from typing import Tuple, Optional
from struct import Struct
from threading import Thread
from multiprocessing import Process, Queue as ProcQueue, Event
import struct
import socket
import select
import asyncio
import traceback
import pyaudio
import time
import av
import cv2

TypeAddress = Tuple[str, int]
TypeCommandData = Optional[Tuple[bool, bool, bool, bool, int]]


class QtClient(Thread):

    def __init__(self, toSAddress: TypeAddress, toRAddress: TypeAddress, logging=True):
        super(QtClient, self).__init__()
        if logging:
            self.__glDebugQueue = ThreadQueue(10)
        else:
            self.__glDebugQueue = None
        self.__glDebug = Debug(self.__glDebugQueue, True, "指令|数据收发客户端(QT)")
        # 全局Debug
        self.__addressToS = toSAddress
        self.__addressToR = toRAddress
        # 发送数据到SocketServer的地址和接受SocketServer数据的地址
        self.__ErrorStatus = Event()
        # 全局错误状态变量，如果变为True, 则结束所有线程任务和其内部的协程
        self.__dataQueue = ThreadQueue(10)
        self.__dataResQueue = ThreadQueue(1)
        self.__dataResult = (
            0, 0.0, 0, 0.0, 0.0, 0, 0
        )
        self.__pack = {
            'up': False,
            'down': False,
            'left': False,
            'right': False,
            'speed': 0,
            'main_video': False,
            'sub_video': False,
            'audio_sent': False,
            'audio_recv': False,
            'client': True
        }
        self.__send_pack = {
            'up': False,
            'down': False,
            'left': False,
            'right': False,
            'speed': 0,
            'main_video': False,
            'sub_video': False,
            'audio_sent': False,
            'audio_recv': False,
            'client': True
        }

    async def __qtSCUDPTask(self):
        self.__glDebug.pushInfo("[指令发送器] 已进入该协程", 'Debug')
        udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        inputs, outputs = [udpSocket], [udpSocket]
        self.__glDebug.pushInfo("[指令发送器] 开始数据发送循环...")
        while not self.__ErrorStatus.is_set():
            try:
                if not self.__dataQueue.empty():
                    datas = self.__dataQueue.get()
                    datalist = [
                        datas['up'],
                        datas['down'],
                        datas['left'],
                        datas['right'],
                        datas['speed'],
                        datas['main_video'],
                        datas['sub_video'],
                        datas['audio_sent'],
                        datas['audio_recv'],
                        datas['client']
                    ]
                    raw_data = struct.Struct("< ? ? ? ? B ? ? ? ? ?").pack(*datalist)
                    udpSocket.sendto(raw_data, self.__addressToS)
                    print(raw_data)
                    await asyncio.sleep(0)
                else:
                    await asyncio.sleep(0)
            except Exception as e:
                self.__glDebug.pushInfo(
                    f"[指令发送器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__glDebug.pushInfo("[指令发送器] 该协程遇到未知错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__glDebug.pushInfo("[指令发送器] 遇到全局错误申请, 退出该协程!", 'Debug')
        self.__ErrorStatus.set()

    async def __qtRDUDPTask(self):
        self.__glDebug.pushInfo("[数据接收器] 已进入该协程", 'Debug')
        udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        udpSocket.bind(self.__addressToR)
        inputs, outputs = [udpSocket], [udpSocket]
        self.__glDebug.pushInfo("[数据接收器] 准备接受服务端数据...")
        while not self.__ErrorStatus.is_set():
            try:
                readable, writable, exceptional = select.select(inputs, outputs, inputs, 0)
                if len(readable) == 0:
                    await asyncio.sleep(0)
                else:
                    raw_data, _ = udpSocket.recvfrom(1024)
                    self.__dataResult = struct.Struct("< B H B H H h d").unpack(raw_data)
                    self.__dataResQueue.put(self.__dataResult)
            except Exception as e:
                self.__glDebug.pushInfo(
                    f"[数据接收器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__glDebug.pushInfo("[数据接收器] 该协程遇到未知错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__glDebug.pushInfo("[数据接收器] 遇到全局错误申请, 退出该协程!", 'Debug')
        self.__ErrorStatus.set()

    def run(self) -> None:
        self.__glDebug.pushInfo("该线程运行开始!", "Debug")
        myself = self

        async def main(__self=myself):
            tasks = [
                __self.__qtSCUDPTask(),
                __self.__qtRDUDPTask()
            ]
            await asyncio.gather(*tasks)

        loop = asyncio.new_event_loop()
        loop.run_until_complete(main())
        self.__ErrorStatus.set()
        self.__glDebug.pushInfo("该线程运行完毕!", "Debug")

    def openClient(self):
        self.start()

    def closeClient(self):
        self.__ErrorStatus.set()

    def setKey(self, key):
        """
        描述：向队列里面放入数据，发送到服务器
        参数：data: 数据
        返回值：无
        """
        self.__send_pack[key] = True
        self.__dataQueue.put(self.__send_pack)

    def clearKey(self, key):
        self.__send_pack[key] = False
        self.__dataQueue.put(self.__send_pack)

    def sentData(self, data):
        if self.__dataQueue.full():
            self.__dataQueue.get()
            self.__dataQueue.put(data)
        else:
            self.__dataQueue.put(data)

    def getData(self):
        """
        描述：获取服务器发送过来的数据
        参数：无
        返回值：无
        """
        if self.__dataResQueue.empty():
            return None
        else:
            return self.__dataResQueue.get()

    def isOpened(self):
        return self.is_alive()


"""
WebClient暂时放弃, 因为图传方案不太适用此种方案
"""


# class WebClient(Thread):
#
#     def __init__(self, toSAddress: TypeAddress, toRAddress: TypeAddress, logging=True):
#         super(WebClient, self).__init__()
#         if logging:
#             self.__glDebugQueue = ThreadQueue(10)
#         else:
#             self.__glDebugQueue = None
#         self.__glDebug = Debug(self.__glDebugQueue, True, "指令|数据收发客户端(Web端)")
#         # 全局Debug
#         self.__addressToS = toSAddress
#         self.__addressToR = toRAddress
#         # 发送数据到SocketServer的地址和接受SocketServer数据的地址
#         self.__ErrorStatus = Event()
#         # 全局错误状态变量，如果变为True, 则结束所有线程任务和其内部的协程
#         self.__dataQueue = ThreadQueue(5)
#         self.__dataResult = (
#             0, 0.0, 0, 0.0, 0.0, 0, 0
#         )
#         self.__pack = {
#             'up': False,
#             'down': False,
#             'left': False,
#             'right': False,
#             'speed': 0,
#             'status': False
#         }
#         self.__send_pack = {
#             'up': False,
#             'down': False,
#             'left': False,
#             'right': False,
#             'speed': 0,
#             'status': False
#         }
#
#     async def __qtSCUDPTask(self):
#         self.__glDebug.pushInfo("[SendCommandTask] 已进入该协程")
#         udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#         inputs, outputs = [udpSocket], []
#         nums = 0
#         self.__glDebug.pushInfo("[SendCommandTask] 准备接受服务端数据...")
#         while True:
#             if self.__ErrorStatus.is_set():
#                 break
#             readable, writable, exceptional = select.select(inputs, outputs, inputs, 0)
#             try:
#                 if len(readable) == 0:
#                     udpSocket.sendto(b'Hello', self.__addressToS)
#                     await asyncio.sleep(1)
#                 else:
#                     self.__glDebug.pushInfo("[SendCommandTask] 服务器连接成功, 准备向服务器发送数据...")
#                     raw_data, _ = udpSocket.recvfrom(1024)
#                     if raw_data == b'World':
#                         self.__glDebug.pushInfo("[SendCommandTask] 已准备向服务器发送数据!")
#                         break
#             except ConnectionResetError:
#                 continue
#         while not self.__ErrorStatus.is_set():
#             try:
#                 if not self.__dataQueue.empty():
#                     datas = self.__dataQueue.get()
#                     datalist = [
#                         datas['up'],
#                         datas['down'],
#                         datas['left'],
#                         datas['right'],
#                         datas['speed'],
#                         datas['status']
#                     ]
#                     raw_data = struct.Struct("< ? ? ? ? B").pack(*datalist)
#                     udpSocket.sendto(raw_data, self.__addressToS)
#                     if datas['status']:
#                         self.__ErrorStatus.set()
#                 else:
#                     await asyncio.sleep(0)
#             except Exception as e:
#                 self.__glDebug.pushInfo(
#                     f"[SendCommandTask] \n{traceback.format_exc()}\n----------Error----------",
#                     "Error"
#                 )
#                 self.__glDebug.pushInfo("[SendCommandTask] 该协程遇到未知错误, 退出!", "Error", style='underline')
#                 break
#         if self.__ErrorStatus.is_set():
#             self.__glDebug.pushInfo("[SendCommandTask] 遇到全局错误申请, 退出该协程!")
#         self.__ErrorStatus.set()
#
#     async def __qtRDUDPTask(self):
#         self.__glDebug.pushInfo("[RecvCommandTask] 已进入该协程")
#         udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#         inputs, outputs = [udpSocket], []
#         nums = 0
#         self.__glDebug.pushInfo("[RecvCommandTask] 准备接受服务端数据...")
#         while True:
#             if self.__ErrorStatus.is_set():
#                 break
#             readable, writable, exceptional = select.select(inputs, outputs, inputs, 0)
#             try:
#                 if len(readable) == 0:
#                     udpSocket.sendto(b'Hello', self.__addressToR)
#                     await asyncio.sleep(1)
#                 else:
#                     self.__glDebug.pushInfo("[RecvCommandTask] 服务器连接成功, 准备接受服务器数据...")
#                     raw_data, _ = udpSocket.recvfrom(1024)
#                     if raw_data == b'World':
#                         self.__glDebug.pushInfo("[RecvCommandTask] 已准备接受服务器发送的数据!")
#                         break
#             except ConnectionResetError:
#                 continue
#         while not self.__ErrorStatus.is_set():
#             try:
#                 inputs, outputs = [udpSocket], []
#                 readable, writable, exceptional = select.select(inputs, outputs, inputs, 0)
#                 if len(readable) == 0:
#                     await asyncio.sleep(0)
#                 else:
#                     clientSock: socket.socket = readable[0]
#                     raw_data, _ = clientSock.recvfrom(1024)
#                     self.__dataResult = struct.Struct("< B H B H H h d").unpack(raw_data)
#             except Exception as e:
#                 self.__glDebug.pushInfo(
#                     f"[RecvCommandTask] \n{traceback.format_exc()}\n----------Error----------",
#                     "Error"
#                 )
#                 self.__glDebug.pushInfo("[RecvCommandTask] 该协程遇到未知错误, 退出!", "Error")
#                 break
#         if self.__ErrorStatus.is_set():
#             self.__glDebug.pushInfo("[RecvCommandTask] 遇到全局错误申请, 退出该协程!")
#         self.__ErrorStatus.set()
#
#     def run(self) -> None:
#         myself = self
#
#         async def main(__self=myself):
#             tasks = [
#                 __self.__qtSCUDPTask(),
#                 __self.__qtRDUDPTask()
#             ]
#             await asyncio.gather(*tasks)
#
#         loop = asyncio.new_event_loop()
#         self.__glDebug.pushInfo("Qt数据接收线程开始运行!")
#         loop.run_until_complete(main())
#         self.__ErrorStatus.set()
#         self.__glDebug.pushInfo("总线程遇到错误，退出该线程！", "Error")
#
#     def openClient(self):
#         self.start()
#
#     def closeClient(self):
#         self.__ErrorStatus.set()
#
#     def setKey(self, key):
#         """
#         描述：向队列里面放入数据，发送到服务器
#         参数：data: 数据
#         返回值：无
#         """
#         self.__send_pack[key] = True
#         self.__dataQueue.put(self.__send_pack)
#
#     def clearKey(self, key):
#         self.__send_pack[key] = False
#         self.__dataQueue.put(self.__send_pack)
#
#     def sentData(self, data):
#         self.__dataQueue.put(data)
#
#     def getData(self):
#         """
#         描述：获取服务器发送过来的数据
#         参数：无
#         返回值：无
#         """
#         return self.__dataResult
#
#     def isOpened(self):
#         return self.is_alive()


class AudioClientRecv(Process):

    def __init__(
            self,
            recv_port: int,
            logging=True
    ):
        super(AudioClientRecv, self).__init__()
        if logging:
            self.__glDebugQueue = ProcQueue(10)
        else:
            self.__glDebugQueue = None
        self.__recvPort = recv_port
        self.__mainThreadDebug = Debug(self.__glDebugQueue, True, "音频接收输出客户端(QT)")
        self.__ErrorStatus = Event()

    def __taskAudioRecv(self, recv_port, recv_channel: ThreadQueue):
        self.__mainThreadDebug.pushInfo("[音频数据接收器] 音频接收协程初始化...")
        server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        server.bind(('169.254.171.1', recv_port))
        # myPoll = select.epoll()
        # myPoll.register(server, select.EPOLLIN)
        self.__mainThreadDebug.pushInfo("[音频数据接收器] 已进入发送循环...")
        while not self.__ErrorStatus.is_set():
            try:
                res, _, _ = select.select([server], [], [], 0)
                if len(res) == 0:
                    pass
                else:
                    raw_data, _ = server.recvfrom(65535)
                    recv_channel.put(raw_data)
            except Exception as e:
                self.__mainThreadDebug.pushInfo(
                    f"[音频数据接收器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__mainThreadDebug.pushInfo("[音频数据接收器] 该协程发生错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__mainThreadDebug.pushInfo("[音频数据接收器] 出现全局错误申请, 退出该协程!", "Warning")
        self.__ErrorStatus.set()

    def __taskAudioOutput(self, stream, recv_channel: ThreadQueue):
        self.__mainThreadDebug.pushInfo("[音频数据输出器] 音频接收协程初始化...")
        self.__mainThreadDebug.pushInfo("[音频数据输出器] 已进入发送循环...")
        while not self.__ErrorStatus.is_set():
            try:
                writeable_nums = stream.get_write_available()
                if writeable_nums == 0:
                    time.sleep(0)
                else:
                    if recv_channel.empty():
                        time.sleep(0)
                    else:
                        raw_data = recv_channel.get()
                        stream.write(raw_data)
            except Exception as e:
                self.__mainThreadDebug.pushInfo(
                    f"[音频数据输出器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__mainThreadDebug.pushInfo("[音频数据输出器] 该协程发生错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__mainThreadDebug.pushInfo("[音频数据输出器] 出现全局错误申请, 退出该协程!", "Warning")
        self.__ErrorStatus.set()

    def run(self) -> None:
        self.__mainThreadDebug.pushInfo("该进程运行开始!", "Debug")
        CHANNELS = 1
        RATE = 44100
        stream = pyaudio.PyAudio().open(
            format=pyaudio.paInt16,
            channels=CHANNELS,
            rate=RATE,
            output=True,
        )
        channel = ThreadQueue(20)
        audioRecv = Thread(
            target=self.__taskAudioRecv,
            args=(self.__recvPort, channel)
        )
        audioOutput = Thread(
            target=self.__taskAudioOutput,
            args=(stream, channel)
        )
        audioRecv.setDaemon(True), audioOutput.setDaemon(True)
        self.__mainThreadDebug.pushInfo("线程运行中...", "Debug")
        audioRecv.start(), audioOutput.start()
        audioRecv.join(), audioOutput.join()
        self.__mainThreadDebug.pushInfo("进程运行完毕!", "Debug")

    def openClient(self):
        self.start()

    def closeClient(self):
        self.__ErrorStatus.set()

    def isOpened(self):
        return self.is_alive()


class AudioClientSent(Process):

    def __init__(
            self,
            sent_port: int,
            sent_address: str = "169.254.171.43",
            logging=True
    ):
        super(AudioClientSent, self).__init__()
        if logging:
            self.__glDebugQueue = ProcQueue(10)
        else:
            self.__glDebugQueue = None
        self.__sentPort = sent_port
        self.__sentAddress = sent_address
        self.__mainThreadDebug = Debug(self.__glDebugQueue, True, "音频采集发送客户端(QT)")
        self.__ErrorStatus = Event()

    def __taskAudioSent(self, sent_port, sent_address, sent_channel: ThreadQueue):
        self.__mainThreadDebug.pushInfo("[音频数据发送器] 音频发送协程初始化...")
        server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.__mainThreadDebug.pushInfo("[音频数据发送器] 已进入发送循环...")
        while not self.__ErrorStatus.is_set():
            try:
                if sent_channel.empty():
                    time.sleep(0)
                else:
                    raw_data = sent_channel.get()
                    server.sendto(raw_data, (sent_address, sent_port))
            except Exception as e:
                self.__mainThreadDebug.pushInfo(
                    f"[音频数据发送器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__mainThreadDebug.pushInfo("[音频数据发送器] 该协程发生错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__mainThreadDebug.pushInfo("[音频数据发送器] 出现全局错误申请, 退出该协程!", "Warning")
        self.__ErrorStatus.set()

    def __taskAudioCapture(self, stream, sent_channel: ThreadQueue):
        self.__mainThreadDebug.pushInfo("[音频数据采集器] 音频采集协程初始化...")
        self.__mainThreadDebug.pushInfo("[音频数据采集器] 已进入采集循环...")
        while not self.__ErrorStatus.is_set():
            try:
                readable_nums = stream.get_read_available()
                if readable_nums == 0:
                    time.sleep(0)
                else:
                    raw_data = stream.read(readable_nums)
                    sent_channel.put(raw_data)
            except Exception as e:
                self.__mainThreadDebug.pushInfo(
                    f"[音频数据采集器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__mainThreadDebug.pushInfo("[音频数据采集器] 该协程发生错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__mainThreadDebug.pushInfo("[音频数据采集器] 出现全局错误申请, 退出该协程!", "Warning")
        self.__ErrorStatus.set()

    def run(self) -> None:
        self.__mainThreadDebug.pushInfo("该进程运行开始!", "Debug")
        CHANNELS = 1
        RATE = 44100
        stream = pyaudio.PyAudio().open(
            format=pyaudio.paInt16,
            channels=CHANNELS,
            rate=RATE,
            input=True
        )
        channel = ThreadQueue(20)
        audioCapture = Thread(
            target=self.__taskAudioCapture,
            args=(stream, channel)
        )
        audioSent = Thread(
            target=self.__taskAudioSent,
            args=(self.__sentPort, self.__sentAddress, channel)
        )
        audioSent.setDaemon(True), audioCapture.setDaemon(True)
        self.__mainThreadDebug.pushInfo("线程运行中...", "Debug")
        audioSent.start(), audioCapture.start()
        audioSent.join(), audioCapture.join()
        self.__mainThreadDebug.pushInfo("进程运行完毕!", "Debug")

    def openClient(self):
        self.start()

    def closeClient(self):
        self.__ErrorStatus.set()

    def isOpened(self):
        return self.is_alive()


class VideoRecvMainClient(Process):

    def __init__(
            self,
            recv_port: int,
            logging=True
    ):
        super(VideoRecvMainClient, self).__init__()
        if logging:
            self.__glDebugQueue = ProcQueue(10)
        else:
            self.__glDebugQueue = None
        self.__recvPort = recv_port
        self.__mainThreadDebug = Debug(self.__glDebugQueue, True, "主视频流接收客户端(QT)")
        self.__procQueue = ProcQueue(10)
        self.__ErrorStatus = Event()

    def __taskRecv(self, recv_port: int, channels: ThreadQueue):
        self.__mainThreadDebug.pushInfo("[视频数据接收器] 视频流接收协程初始化...")
        server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        server.bind(('169.254.171.1', recv_port))
        # myPoll = select.epoll()
        # myPoll.register(server, select.EPOLLIN)
        self.__mainThreadDebug.pushInfo("[视频数据接收器] 已进入接收循环...")
        lastTime = time.time()
        while not self.__ErrorStatus.is_set():
            try:
                # res = myPoll.poll(0)
                res, _, _ = select.select([server], [], [], 0)
                if len(res) == 0:
                    time.sleep(0)
                else:
                    recvBytes, _ = server.recvfrom(65535)
                    # print(len(recvBytes))
                    nowTime = time.time()
                    # print(nowTime - lastTime)
                    lastTime = nowTime
                    channels.put(recvBytes)
            except Exception as e:
                self.__mainThreadDebug.pushInfo(
                    f"[视频数据接收器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__mainThreadDebug.pushInfo("[视频数据接收器] 该协程发生错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__mainThreadDebug.pushInfo("[视频数据接收器] 出现全局错误申请, 退出该协程!", "Warning")
        self.__ErrorStatus.set()

    def __taskUpdate(self, channels: ThreadQueue, main_channels: ProcQueue):
        self.__mainThreadDebug.pushInfo("[视频数据解码器] 视频流更新协程初始化...")
        codec = av.CodecContext.create('h264', 'r')
        self.__mainThreadDebug.pushInfo("[视频数据解码器] 已进入更新循环...")
        while not self.__ErrorStatus.is_set():
            try:
                if channels.empty():
                    time.sleep(0)
                else:
                    chunk = channels.get()
                    # print(f"帧数：{meta_chunk}|延时：{round((time.time() - timeTemp) * 1000, 2)}")
                    packets = codec.parse(chunk)
                    for packet in packets:
                        frames = codec.decode(packet)
                        for frame in frames:
                            frame = frame.reformat(format="bgr24")
                            frame = frame.to_ndarray()
                            main_channels.put(frame)
            except Exception as e:
                self.__mainThreadDebug.pushInfo(
                    f"[视频数据解码器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__mainThreadDebug.pushInfo("[视频数据解码器] 该协程发生错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__mainThreadDebug.pushInfo("[视频数据解码器] 出现全局错误申请, 退出该协程!", "Warning")
        self.__ErrorStatus.set()

    def run(self) -> None:
        self.__mainThreadDebug.pushInfo("该进程运行开始!", "Debug")
        recv_port = self.__recvPort
        proc_queue = self.__procQueue
        channel = ThreadQueue(10)
        recvThread = Thread(
            target=self.__taskRecv,
            args=(recv_port, channel)
        )
        sentThread = Thread(
            target=self.__taskUpdate,
            args=(channel, proc_queue)
        )
        recvThread.setDaemon(True)
        recvThread.start()
        sentThread.setDaemon(True)
        sentThread.start()
        self.__mainThreadDebug.pushInfo("线程运行中...", "Debug")
        recvThread.join()
        sentThread.join()
        self.__mainThreadDebug.pushInfo("进程运行完毕!", "Debug")

    def openClient(self):
        self.start()

    def closeClient(self):
        self.__ErrorStatus.set()

    def isOpened(self):
        return self.is_alive()

    def read(self):
        if self.__procQueue.empty():
            return None
        else:
            res = self.__procQueue.get()
            return res

    def isEmpty(self):
        return self.__procQueue.empty()


class VideoRecvSubClient(Process):

    def __init__(
            self,
            recv_port: int,
            logging=True
    ):
        super(VideoRecvSubClient, self).__init__()
        if logging:
            self.__glDebugQueue = ProcQueue(10)
        else:
            self.__glDebugQueue = None
        self.__recvPort = recv_port
        self.__mainThreadDebug = Debug(self.__glDebugQueue, True, "副视频流接收客户端(QT)")
        self.__procQueue = ProcQueue(10)
        self.__ErrorStatus = Event()

    def __taskRecv(self, recv_port: int, channels: ThreadQueue):
        self.__mainThreadDebug.pushInfo("[视频数据接收器] 视频流接收协程初始化...")
        server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        server.bind(('169.254.171.1', recv_port))
        # myPoll = select.epoll()
        # myPoll.register(server, select.EPOLLIN)
        self.__mainThreadDebug.pushInfo("[视频数据接收器] 已进入接收循环...")
        while not self.__ErrorStatus.is_set():
            try:
                # res = myPoll.poll(0)
                res, _, _ = select.select([server], [], [], 0)
                if len(res) == 0:
                    pass
                else:
                    recvBytes, _ = server.recvfrom(65535)
                    channels.put(recvBytes)
            except Exception as e:
                self.__mainThreadDebug.pushInfo(
                    f"[视频数据接收器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__mainThreadDebug.pushInfo("[视频数据接收器] 该协程发生错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__mainThreadDebug.pushInfo("[视频数据接收器] 出现全局错误申请, 退出该协程!")
        self.__ErrorStatus.set()

    def __taskUpdate(self, channels: ThreadQueue, main_channels: ProcQueue):
        self.__mainThreadDebug.pushInfo("[视频数据解码器] 视频流更新协程初始化...")
        codec = av.CodecContext.create('h264', 'r')
        self.__mainThreadDebug.pushInfo("[视频数据解码器] 已进入更新循环...")
        while not self.__ErrorStatus.is_set():
            try:
                chunk = channels.get()
                # print(f"帧数：{meta_chunk}|延时：{round((time.time() - timeTemp) * 1000, 2)}")
                packets = codec.parse(chunk)
                for packet in packets:
                    frames = codec.decode(packet)
                    for frame in frames:
                        frame = frame.reformat(format="bgr24")
                        frame = frame.to_ndarray()
                        main_channels.put(frame)
            except Exception as e:
                self.__mainThreadDebug.pushInfo(
                    f"[视频数据解码器] \n{traceback.format_exc()}\n----------Error----------",
                    "Error"
                )
                self.__mainThreadDebug.pushInfo("[视频数据解码器] 该协程发生错误, 退出!", "Error")
                break
        if self.__ErrorStatus.is_set():
            self.__mainThreadDebug.pushInfo("[视频数据解码器] 出现全局错误申请, 退出该协程!")
        self.__ErrorStatus.set()

    def run(self) -> None:
        self.__mainThreadDebug.pushInfo("该进程运行开始!", "Debug")
        recv_port = self.__recvPort
        proc_queue = self.__procQueue
        channel = ThreadQueue(10)
        recvThread = Thread(
            target=self.__taskRecv,
            args=(recv_port, channel)
        )
        sentThread = Thread(
            target=self.__taskUpdate,
            args=(channel, proc_queue)
        )
        recvThread.setDaemon(True)
        recvThread.start()
        sentThread.setDaemon(True)
        sentThread.start()
        self.__mainThreadDebug.pushInfo("线程运行中...", "Debug")
        recvThread.join()
        sentThread.join()
        self.__mainThreadDebug.pushInfo("进程运行完毕!", "Debug")

    def openClient(self):
        self.start()

    def closeClient(self):
        self.__ErrorStatus.set()

    def isOpened(self):
        return self.is_alive()

    def read(self):
        if self.__procQueue.empty():
            return None
        else:
            res = self.__procQueue.get()
            return res

    def isEmpty(self):
        return self.__procQueue.empty()




if __name__ == '__main__':
    # socketClient = QtClient(
    #     toSAddress=('localhost', 8101),
    #     toRAddress=('localhost', 8100),
    #     logging=True
    # )
    # socketClient.setDaemon(True)
    # socketClient.openClient()
    # print("20秒后关闭服务器")
    # while True:
    #     data = input("key:")
    #     if data == 'exit':
    #         break
    #     socketClient.setKey(data)
    # socketClient.closeClient()
    # socketClient.join()

    videoMainServer = VideoRecvMainClient(8103)
    videoSubServer = VideoRecvSubClient(8104)
    videoMainServer.openClient()
    videoSubServer.openClient()
    while True:
        try:
            lastTime = time.time()
            frame_main = videoMainServer.read()
            frame_sub = videoSubServer.read()
            nowTime = time.time()
            if frame_main is not None:
                cv2.imshow("MainWindow", frame_main)
            if frame_sub is not None:
                cv2.imshow('SubWindow', frame_sub)
            key = cv2.waitKey(1) & 0xFF
            if key == ord("q"):
                break
        except KeyboardInterrupt:
            break
    videoMainServer.closeClient()
    videoSubServer.closeClient()
    videoMainServer.join()
    videoSubServer.join()

    # audioClientSent = AudioClientSent(sent_port=8211)
    # audioClientRecv = AudioClientRecv(recv_port=8212)
    # audioClientSent.start(), audioClientRecv.start()
    # while audioClientSent.isOpened() and audioClientRecv.isOpened():
    #     try:
    #         pass
    #     except KeyboardInterrupt:
    #         break
    # audioClientSent.closeServer()
    # audioClientRecv.closeServer()
    # audioClientSent.join()
    # audioClientRecv.join()
